//============================================================================
// Name        : Socket.cpp
// Author      : Game std
// Version     :
// Copyright   : LGPL
// Description : Hello World in C++, Ansi-style
//============================================================================

//

#include <boost/asio.hpp>
//#include <boost/array.hpp>
//#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>
#include <iostream>
#include <string>

#define PORT_NUMBER 8080
#define SERVER_ADDRESS 0x7F000001
#define MAX_SOCKETS 32

class CClient
{
private:
public:
	CClient(){};
	~CClient(){};
	void CReader( const boost::system::error_code &ec );
	void CWriter( const boost::system::error_code &ec );
	boost::asio::ip::tcp::socket* _socket;
};

boost::asio::io_service io_service;
boost::asio::ip::tcp::acceptor *acceptor;
boost::asio::ip::tcp::socket* ArrayPsockets[MAX_SOCKETS];
CClient ClientArray[MAX_SOCKETS];
boost::asio::ip::tcp::endpoint *endpoint;
boost::array<char, 4096> buffer;
int CurrentSocketNumber = 0;

std::string SERVER_VERSION = "Server Version 1.0";
std::string SERVER_MESSAGE = "Message from server";
std::string CLIENT_MESSAGE = "Message from client";
std::string CLIENT_GREETING = "Hellow server!";

void write_handler(const boost::system::error_code &ec )
{
	std::cout << "Message is sent\n";
}

void CClient::CReader(const boost::system::error_code &ec )
{
  std::cout << "CClient::CReader\n";
  if (!ec)
  {
	std::cout << std::string(buffer.data() ) << std::endl;
	_socket->async_receive( boost::asio::buffer(buffer), boost::bind(&CClient::CReader, this,
							boost::asio::placeholders::error) );
    std::cout << "Waiting for next message\n";
  }
}

void CClient::CWriter( const boost::system::error_code &ec )
{
	std::cout << "CClient::CWriter\n";
}

void read_handler(const boost::system::error_code &ec )
{
  std::cout << "read_handler\n";
  if (!ec)
  {
    std::cout << std::string(buffer.data() ) << std::endl;
    ArrayPsockets[0]->async_receive(boost::asio::buffer(buffer), boost::bind( read_handler,
			 boost::asio::placeholders::error) );
    std::cout << "Waiting for next message\n";
  }
}

void connect_handler(const boost::system::error_code &ec)
{
	std::cout << "Connecting now\n";
	if (!ec)
	{
		std::cout << "Estabilishing connection...\n";
		boost::asio::async_write(*(ArrayPsockets[0]), boost::asio::buffer( CLIENT_GREETING ),
						                 boost::bind( write_handler,  boost::asio::placeholders::error ) );
		ArrayPsockets[0]->async_receive(boost::asio::buffer(buffer), boost::bind( read_handler,
				 boost::asio::placeholders::error) );
	}
}

void
accept_handler( const boost::system::error_code &ec )
{
	std::cout << "Somebody connecting\n";
	if (!ec)
	{
		std::cout << "Establishing connection...\n";//SERVER_VERSION

		ArrayPsockets[CurrentSocketNumber]->async_receive( boost::asio::buffer(buffer),
				boost::bind(&CClient::CReader, &ClientArray[CurrentSocketNumber],
						 boost::asio::placeholders::error) );
	    boost::asio::async_write(*(ArrayPsockets[CurrentSocketNumber]), boost::asio::buffer( SERVER_VERSION ),
								     boost::bind( &CClient::CWriter , &ClientArray[CurrentSocketNumber],
								         boost::asio::placeholders::error ) );

		CurrentSocketNumber++;
		boost::asio::ip::tcp::socket *pSocket;
		pSocket = new boost::asio::ip::tcp::socket( io_service );
		ClientArray[CurrentSocketNumber]._socket = ArrayPsockets[CurrentSocketNumber] = pSocket;
		acceptor->async_accept( *(ArrayPsockets[CurrentSocketNumber]), boost::bind( accept_handler,
	               boost::asio::placeholders::error ) );
	}
}

void
WaitForIncomeConnections()
{
		acceptor->listen();
		std::cout << "ArrayPsockets[" << CurrentSocketNumber << "] \n";
		acceptor->async_accept(*(ArrayPsockets[CurrentSocketNumber]), boost::bind( accept_handler,
				               boost::asio::placeholders::error ) );
}

int main()
{
	std::cout << "Client - 0, Server - 1: ";
	int inValue = 0;
	std::cin >> inValue;
	boost::asio::ip::tcp::socket *pSocket;
	pSocket = new boost::asio::ip::tcp::socket( io_service );
	ClientArray[CurrentSocketNumber]._socket = ArrayPsockets[CurrentSocketNumber] = pSocket;

	if ( 0 == inValue )
	{
		int PortNumber;
		std::cout << "Enter server address: ";
		std::cin >> buffer.data();
		std::cout << "Enter port number: ";
		std::cin >> PortNumber;
		endpoint = new boost::asio::ip::tcp::endpoint (
				boost::asio::ip::address_v4::from_string(buffer.data()), PortNumber );

		ArrayPsockets[0]->async_connect( *(endpoint), connect_handler);
		boost::thread IoservRun(boost::bind(&boost::asio::io_service::run, &io_service));
		std::cout << "Enter 0 to stop client\n";
		inValue = -1;
		while ( 0 != inValue )
		{
			std::cin >> inValue;
			std::cout << "Write smt\n";
			boost::asio::async_write( *(ArrayPsockets[0]), boost::asio::buffer( CLIENT_MESSAGE ),
					boost::bind( write_handler,  boost::asio::placeholders::error ));
		}
		ArrayPsockets[0]->close();
		IoservRun.join();
	}
	else
	{
		int PortNumber;
		std::cout << "Enter port number: ";
		std::cin >> PortNumber;
		endpoint = new boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(),
				                                       PortNumber);
		acceptor = new boost::asio::ip::tcp::acceptor(io_service, *endpoint);

		WaitForIncomeConnections( );
		boost::thread IoservRun(boost::bind(&boost::asio::io_service::run, &io_service));
		inValue = -1;
		std::cout << "Enter 0 to stop server\n";
		while ( 0 != inValue )
		{
			std::cin >> inValue;
			for ( int i = 0; i < CurrentSocketNumber; i++ )
			{
				std::cout << "Sending message for client " << i << "\n";
			    ArrayPsockets[i]->async_send( boost::asio::buffer( SERVER_MESSAGE ),
						boost::bind( &CClient::CWriter , &ClientArray[CurrentSocketNumber],  boost::asio::placeholders::error ) );
			}
		}
		acceptor->close();
		IoservRun.join();
	}
	std::cout << "Good buy! \n";
}
